package org.finesys.common.security.client.http;

import static org.finesys.common.constants.SecurityConstants.TENANT_ID;
import static org.finesys.common.security.client.core.constants.GiteeEndpointConstants.TOKEN_URL;

import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.finesys.commom.jackson.util.JacksonUtil;
import org.finesys.common.constants.SecurityConstants;
import org.finesys.common.core.constants.ResultCode;
import org.finesys.common.core.module.R;
import org.finesys.common.core.util.WebUtil;
import org.finesys.common.security.client.core.constants.GiteeEndpointConstants;
import org.finesys.common.security.client.core.endpoint.OAuth2GiteeParameterNames;
import org.finesys.common.security.client.exception.StateGiteeException;
import org.finesys.common.security.client.properties.GiteeProperties;
import org.finesys.common.security.client.service.GiteeRegisteredClientRepository;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.DefaultMapOAuth2AccessTokenResponseConverter;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.type.TypeReference;

import cn.hutool.core.text.StrPool;
import lombok.AllArgsConstructor;

@AllArgsConstructor
public class GiteeCodeHttpFilter implements Filter {

    private final GiteeRegisteredClientRepository giteeRegisteredClientRepository;
    public static final String PREFIX_URL = "/gitee/code";

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getRequestURI();
        //码云ID
        String appId = uri.replace(PREFIX_URL + StrPool.SLASH, "");
        String code = request.getParameter(OAuth2ParameterNames.CODE);
        String state = request.getParameter(OAuth2ParameterNames.STATE);
        String binding = request.getParameter(OAuth2GiteeParameterNames.BINDING);
        boolean stateValid = giteeRegisteredClientRepository.stateValid(appId, code, state);
        if (!stateValid) {
            OAuth2Error oAuth2Error = new OAuth2Error(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), "非法状态码", GiteeEndpointConstants.AUTHORIZE_URL);
            throw new StateGiteeException(oAuth2Error);
        }
        GiteeProperties.Gitee gitee = giteeRegisteredClientRepository.getGiteeByAppId(appId);
        Map<String, String> uriVariables = new HashMap<>(8);
        uriVariables.put(OAuth2ParameterNames.GRANT_TYPE, SecurityConstants.GITEE);
        uriVariables.put(OAuth2GiteeParameterNames.APPID, appId);
        uriVariables.put(OAuth2GiteeParameterNames.BINDING, binding);
        uriVariables.put(OAuth2ParameterNames.CODE, code);
        uriVariables.put(OAuth2ParameterNames.STATE, state);
        uriVariables.put(OAuth2ParameterNames.CLIENT_ID, gitee.getClientId());
        uriVariables.put(OAuth2ParameterNames.CLIENT_SECRET, gitee.getClientSecret());

        //请求OAuth2内置的授权端点
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<?> httpEntity = new HttpEntity<>(headers);
        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter());
        String res = restTemplate.postForObject(gitee.getTokenUrl() + TOKEN_URL, httpEntity, String.class, uriVariables);
        Map<String, Object> reponseMap = JacksonUtil.parseObject(res, new TypeReference<Map<String, Object>>() {
        });
        if (reponseMap == null) {
            return;
        }
        String resCode = (String) reponseMap.get("code");
        if (resCode == null) {
            DefaultMapOAuth2AccessTokenResponseConverter converter = new DefaultMapOAuth2AccessTokenResponseConverter();
            OAuth2AccessTokenResponse oAuth2AccessTokenResponse = converter.convert(reponseMap);

            OAuth2AccessToken oAuth2AccessToken = oAuth2AccessTokenResponse.getAccessToken();
            OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessTokenResponse.getRefreshToken();
            String tenantId = String.valueOf(oAuth2AccessTokenResponse.getAdditionalParameters().get(TENANT_ID));

            String successURL = gitee.getSuccessRedirectUrl();
            String accessToken = oAuth2AccessToken.getTokenValue();
            Instant expiresAt = oAuth2AccessToken.getExpiresAt();
            String refreshToken = "";
            Instant refreshTokenExpiresAt = null;
            if (oAuth2RefreshToken != null) {
                refreshToken = oAuth2RefreshToken.getTokenValue();
                refreshTokenExpiresAt = oAuth2RefreshToken.getExpiresAt();
            }
            response.sendRedirect(String.format("%s?store=true&accessToken=%s&refreshToken=%s&tenantId=%s&expiresAt=%s&refreshTokenExpiresAt=%s", successURL, accessToken, refreshToken,tenantId, expiresAt, refreshTokenExpiresAt));
        } else {
            //存在响应代码时，代表程序错误，直接返回错误内容
            WebUtil.makeFailureResponse(response, R.failed());
        }
    }
}
